第49步 深度学习图像识别:Vision Transformer建模(Pytorch) 您所在的位置:网站首页 pytorch 关系抽取 第49步 深度学习图像识别:Vision Transformer建模(Pytorch)

第49步 深度学习图像识别:Vision Transformer建模(Pytorch)

2023-07-12 05:06| 来源: 网络整理| 查看: 265

基于WIN10的64位系统演示

一、写在前面

(1)Transformer

Transformer是一种深度学习模型,它于2017年在"Attention is All You Need"一文中被提出,最初被设计用于处理自然语言处理任务,例如机器翻译。Transformer模型的关键特性是它的自注意力机制(Self-Attention Mechanism),该机制能够捕获输入数据的全局依赖关系,也就是说,它能关注到输入数据中所有位置的信息。自注意力机制使Transformer模型能够捕获输入之间的复杂交互,包括长距离依赖,这在处理文本等序列数据时尤其有用。

(2)Vision Transformer

那么,为什么Transformer模型会被应用在图像识别领域呢?主要有以下几个原因:

(a)全局特性捕获:Transformer的自注意力机制能够捕获全局的信息,这对于理解图像的整体结构和上下文关系非常有用。相比之下,传统的卷积神经网络(CNN)主要是利用局部卷积操作来处理图像,可能会忽视一些全局信息。

(b)序列化处理图像:在图像识别任务中,可以将图像分解为一系列的patch,每一个patch可以看作是图像的一个“词”。这样,我们就可以利用Transformer的自注意力机制去处理这些patch。这种处理方式被称为"Vision Transformer",或者简称为ViT,也就是今天我们要演示的模型。

(c)更好的性能:Transformer模型在自然语言处理领域已经取得了很大的成功,并在一些任务上超越了传统的CNN模型。因此,研究者们开始尝试将其应用到图像识别领域,看看是否可以获得类似的成功。

(d)可解释性:由于自注意力机制,我们可以更好地理解模型的决策过程,知道模型在进行决策时主要关注了图像的哪些部分。

值得注意的是,尽管Transformer在一些计算机视觉任务中表现出色,但这并不意味着它会完全取代CNN。实际上,很多最新的研究都在探索如何将这两种方法结合起来,以发挥它们各自的优点。例如,CNN可以用于提取局部特征,而Transformer可以用于建模这些特征之间的关系。

(3)Vision Transformer的预训练版本

本文继续使用Facebook的高级深度学习框架PyTorchImageModels (timm)。该库提供了多种预训练的Vision Transformer,举几个例子:

(a)vit_base_patch16_224:这是一个基础版本的ViT,它将图像划分为16x16的patch,并且接受224x224大小的图像作为输入。

(b)vit_large_patch16_224:这是一个更大版本的ViT,它同样将图像划分为16x16的patch,但在模型的大小(包括模型的深度和宽度)上更大,因此需要更多的计算资源。

二、Vision Transformer迁移学习代码实战

我们继续胸片的数据集:肺结核病人和健康人的胸片的识别。其中,肺结核病人700张,健康人900张,分别存入单独的文件夹中。

(a)导入包

import copy import torch import torchvision import torchvision.transforms as transforms from torchvision import models from torch.utils.data import DataLoader from torch import optim, nn from torch.optim import lr_scheduler import os import matplotlib.pyplot as plt import warnings import numpy as np warnings.filterwarnings("ignore") plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False # 设置GPU device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

(b)导入数据集

import torch from torchvision import datasets, transforms import os # 数据集路径 data_dir = "./MTB" # 图像的大小 img_height = 100 img_width = 100 # 数据预处理 data_transforms = { 'train': transforms.Compose([ transforms.RandomResizedCrop(img_height), transforms.RandomHorizontalFlip(), transforms.RandomVerticalFlip(), transforms.RandomRotation(0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), 'val': transforms.Compose([ transforms.Resize((img_height, img_width)), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]), } # 加载数据集 full_dataset = datasets.ImageFolder(data_dir) # 获取数据集的大小 full_size = len(full_dataset) train_size = int(0.7 * full_size) # 假设训练集占80% val_size = full_size - train_size # 验证集的大小 # 随机分割数据集 torch.manual_seed(0) # 设置随机种子以确保结果可重复 train_dataset, val_dataset = torch.utils.data.random_split(full_dataset, [train_size, val_size]) # 将数据增强应用到训练集 train_dataset.dataset.transform = data_transforms['train'] # 创建数据加载器 batch_size = 32 train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4) val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=True, num_workers=4) dataloaders = {'train': train_dataloader, 'val': val_dataloader} dataset_sizes = {'train': len(train_dataset), 'val': len(val_dataset)} class_names = full_dataset.classes

(c)导入Vision Transformer

# 定义Vision Transformer模型 import timm model = timm.create_model('vit_base_patch16_224', pretrained=True) # 你可以选择适合你需求的Vision Transformer版本,这里以vit_base_patch16_224为例 num_ftrs = model.head.in_features # 根据分类任务修改最后一层 model.head = nn.Linear(num_ftrs, len(class_names)) model = model.to(device) # 打印模型摘要 print(model)

(d)编译模型

# 定义损失函数 criterion = nn.CrossEntropyLoss() # 定义优化器 optimizer = optim.Adam(model.parameters()) # 定义学习率调度器 exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1) # 开始训练模型 num_epochs = 10 best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 # 初始化记录器 train_loss_history = [] train_acc_history = [] val_loss_history = [] val_acc_history = [] for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch, num_epochs - 1)) print('-' * 10) # 每个epoch都有一个训练和验证阶段 for phase in ['train', 'val']: if phase == 'train': model.train() # Set model to training mode else: model.eval() # Set model to evaluate mode running_loss = 0.0 running_corrects = 0 # 遍历数据 for inputs, labels in dataloaders[phase]: inputs = inputs.to(device) labels = labels.to(device) # 零参数梯度 optimizer.zero_grad() # 前向 with torch.set_grad_enabled(phase == 'train'): outputs = model(inputs) _, preds = torch.max(outputs, 1) loss = criterion(outputs, labels) # 只在训练模式下进行反向和优化 if phase == 'train': loss.backward() optimizer.step() # 统计 running_loss += loss.item() * inputs.size(0) running_corrects += torch.sum(preds == labels.data) epoch_loss = running_loss / dataset_sizes[phase] epoch_acc = (running_corrects.double() / dataset_sizes[phase]).item() # 记录每个epoch的loss和accuracy if phase == 'train': train_loss_history.append(epoch_loss) train_acc_history.append(epoch_acc) else: val_loss_history.append(epoch_loss) val_acc_history.append(epoch_acc) print('{} Loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc)) # 深拷贝模型 if phase == 'val' and epoch_acc > best_acc: best_acc = epoch_acc best_model_wts = copy.deepcopy(model.state_dict()) print() print('Best val Acc: {:4f}'.format(best_acc))

(e)Accuracy和Loss可视化

epoch = range(1, len(train_loss_history)+1) fig, ax = plt.subplots(1, 2, figsize=(10,4)) ax[0].plot(epoch, train_loss_history, label='Train loss') ax[0].plot(epoch, val_loss_history, label='Validation loss') ax[0].set_xlabel('Epochs') ax[0].set_ylabel('Loss') ax[0].legend() ax[1].plot(epoch, train_acc_history, label='Train acc') ax[1].plot(epoch, val_acc_history, label='Validation acc') ax[1].set_xlabel('Epochs') ax[1].set_ylabel('Accuracy') ax[1].legend() #plt.savefig("loss-acc.pdf", dpi=300,format="pdf")

观察模型训练情况:

 蓝色为训练集,橙色为验证集。

(f)混淆矩阵可视化以及模型参数

from sklearn.metrics import classification_report, confusion_matrix import math import pandas as pd import numpy as np import seaborn as sns from matplotlib.pyplot import imshow # 定义一个绘制混淆矩阵图的函数 def plot_cm(labels, predictions): # 生成混淆矩阵 conf_numpy = confusion_matrix(labels, predictions) # 将矩阵转化为 DataFrame conf_df = pd.DataFrame(conf_numpy, index=class_names ,columns=class_names) plt.figure(figsize=(8,7)) sns.heatmap(conf_df, annot=True, fmt="d", cmap="BuPu") plt.title('Confusion matrix',fontsize=15) plt.ylabel('Actual value',fontsize=14) plt.xlabel('Predictive value',fontsize=14) def evaluate_model(model, dataloader, device): model.eval() # 设置模型为评估模式 true_labels = [] pred_labels = [] # 遍历数据 for inputs, labels in dataloader: inputs = inputs.to(device) labels = labels.to(device) # 前向 with torch.no_grad(): outputs = model(inputs) _, preds = torch.max(outputs, 1) true_labels.extend(labels.cpu().numpy()) pred_labels.extend(preds.cpu().numpy()) return true_labels, pred_labels # 获取预测和真实标签 true_labels, pred_labels = evaluate_model(model, dataloaders['val'], device) # 计算混淆矩阵 cm_val = confusion_matrix(true_labels, pred_labels) a_val = cm_val[0,0] b_val = cm_val[0,1] c_val = cm_val[1,0] d_val = cm_val[1,1] # 计算各种性能指标 acc_val = (a_val+d_val)/(a_val+b_val+c_val+d_val) # 准确率 error_rate_val = 1 - acc_val # 错误率 sen_val = d_val/(d_val+c_val) # 灵敏度 sep_val = a_val/(a_val+b_val) # 特异度 precision_val = d_val/(b_val+d_val) # 精确度 F1_val = (2*precision_val*sen_val)/(precision_val+sen_val) # F1值 MCC_val = (d_val*a_val-b_val*c_val) / (np.sqrt((d_val+b_val)*(d_val+c_val)*(a_val+b_val)*(a_val+c_val))) # 马修斯相关系数 # 打印出性能指标 print("验证集的灵敏度为:", sen_val, "验证集的特异度为:", sep_val, "验证集的准确率为:", acc_val, "验证集的错误率为:", error_rate_val, "验证集的精确度为:", precision_val, "验证集的F1为:", F1_val, "验证集的MCC为:", MCC_val) # 绘制混淆矩阵 plot_cm(true_labels, pred_labels) # 获取预测和真实标签 train_true_labels, train_pred_labels = evaluate_model(model, dataloaders['train'], device) # 计算混淆矩阵 cm_train = confusion_matrix(train_true_labels, train_pred_labels) a_train = cm_train[0,0] b_train = cm_train[0,1] c_train = cm_train[1,0] d_train = cm_train[1,1] acc_train = (a_train+d_train)/(a_train+b_train+c_train+d_train) error_rate_train = 1 - acc_train sen_train = d_train/(d_train+c_train) sep_train = a_train/(a_train+b_train) precision_train = d_train/(b_train+d_train) F1_train = (2*precision_train*sen_train)/(precision_train+sen_train) MCC_train = (d_train*a_train-b_train*c_train) / (math.sqrt((d_train+b_train)*(d_train+c_train)*(a_train+b_train)*(a_train+c_train))) print("训练集的灵敏度为:",sen_train, "训练集的特异度为:",sep_train, "训练集的准确率为:",acc_train, "训练集的错误率为:",error_rate_train, "训练集的精确度为:",precision_train, "训练集的F1为:",F1_train, "训练集的MCC为:",MCC_train) # 绘制混淆矩阵 plot_cm(train_true_labels, train_pred_labels)

效果不错:

 (g)AUC曲线绘制

from sklearn import metrics import numpy as np import matplotlib.pyplot as plt from matplotlib.pyplot import imshow from sklearn.metrics import classification_report, confusion_matrix import seaborn as sns import pandas as pd import math def plot_roc(name, labels, predictions, **kwargs): fp, tp, _ = metrics.roc_curve(labels, predictions) plt.plot(fp, tp, label=name, linewidth=2, **kwargs) plt.plot([0, 1], [0, 1], color='orange', linestyle='--') plt.xlabel('False positives rate') plt.ylabel('True positives rate') ax = plt.gca() ax.set_aspect('equal') # 确保模型处于评估模式 model.eval() train_ds = dataloaders['train'] val_ds = dataloaders['val'] val_pre_auc = [] val_label_auc = [] for images, labels in val_ds: for image, label in zip(images, labels): img_array = image.unsqueeze(0).to(device) # 在第0维增加一个维度并将图像转移到适当的设备上 prediction_auc = model(img_array) # 使用模型进行预测 val_pre_auc.append(prediction_auc.detach().cpu().numpy()[:,1]) val_label_auc.append(label.item()) # 使用Tensor.item()获取Tensor的值 auc_score_val = metrics.roc_auc_score(val_label_auc, val_pre_auc) train_pre_auc = [] train_label_auc = [] for images, labels in train_ds: for image, label in zip(images, labels): img_array_train = image.unsqueeze(0).to(device) prediction_auc = model(img_array_train) train_pre_auc.append(prediction_auc.detach().cpu().numpy()[:,1]) # 输出概率而不是标签! train_label_auc.append(label.item()) auc_score_train = metrics.roc_auc_score(train_label_auc, train_pre_auc) plot_roc('validation AUC: {0:.4f}'.format(auc_score_val), val_label_auc , val_pre_auc , color="red", linestyle='--') plot_roc('training AUC: {0:.4f}'.format(auc_score_train), train_label_auc, train_pre_auc, color="blue", linestyle='--') plt.legend(loc='lower right') #plt.savefig("roc.pdf", dpi=300,format="pdf") print("训练集的AUC值为:",auc_score_train, "验证集的AUC值为:",auc_score_val)

ROC曲线如下:

 优秀的ROC曲线!

三、写在最后

最直观的感受就是运算量太大了,一次迭代大约要花3分钟,之前轻量级别的CNN网络也就十几秒;而且消耗的计算资源也很大,机子在跑数据的时候,其他啥都不能干。这是因为Transformer模型的自注意力机制计算复杂度较高,且ViT模型通常在大规模数据集上预训练。

四、数据

链接:https://pan.baidu.com/s/15vSVhz1rQBtqNkNp2GQyVw?pwd=x3jf

提取码:x3jf



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有